package com.mihuo.config.interceptor;

import cn.hutool.json.JSONUtil;
import com.mihuo.compoent.RedisUtil;
import com.mihuo.entity.annotation.Anonymous;
import com.mihuo.entity.common.AuthUser;
import com.mihuo.entity.common.R;
import com.mihuo.entity.constant.RedisKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * Token 鉴权拦截器
 *
 * 功能说明：
 * 1. 检查请求处理方法是否带有 @Anonymous 注解，有则跳过鉴权
 * 2. 对需要鉴权的请求进行 Token 验证
 * 3. 将用户信息存入请求属性供后续使用
 *
 * @author mihuo
 * @version 1.0
 */
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Resource
    private RedisUtil redisUtil;

    /**
     * 在请求处理之前进行拦截处理
     *
     * @param request HttpServletRequest 对象
     * @param response HttpServletResponse 对象
     * @param handler 处理请求的处理器对象
     * @return true-继续执行后续流程，false-中断请求
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 1. 检查是否为预检请求（OPTIONS），如果是则直接放行
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }

        // 2. 检查请求是否应该跳过鉴权（通过 @Anonymous 注解标记）
        if (shouldSkipAuth(handler)) {
            log.debug("请求方法标记为匿名访问，跳过鉴权");
            request.setAttribute("SKIP_AUTH", true);
            return true;
        }

        // 3. 从请求头中获取 Token
        String token = extractTokenFromRequest(request);
        if (token == null) {
            log.warn("请求未携带Token: {}", request.getRequestURI());
            return sendUnauthorizedResponse(response, "未提供访问令牌,或访问令牌不正确");
        }

        // 4. 验证 Token 有效性
        if (!validateToken(token)) {
            log.warn("Token验证失败: {}", token);
            return sendUnauthorizedResponse(response, "令牌无效或已过期");
        }

        // 5. 解析 Token 获取用户信息并存入请求属性
        AuthUser userInfo = (AuthUser) redisUtil.get(RedisKey.LOGIN_USER + token);
        if (userInfo != null) {
            request.setAttribute("CURRENT_USER", userInfo);
        }

        return true;
    }

    /**
     * 判断请求是否应该跳过鉴权检查
     *
     * @param handler 请求处理器
     * @return true-跳过鉴权，false-需要鉴权
     */
    private boolean shouldSkipAuth(Object handler) {
        // 只对 HandlerMethod 类型进行注解检查
        if (!(handler instanceof HandlerMethod)) {
            return false;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        // 检查方法上是否有 @Anonymous 注解
        if (AnnotationUtils.findAnnotation(method, Anonymous.class) != null) {
            return true;
        }

        // 检查类上是否有 @Anonymous 注解
        Class<?> beanType = handlerMethod.getBeanType();
        return AnnotationUtils.findAnnotation(beanType, Anonymous.class) != null;
    }

    /**
     * 从请求中提取 Token
     *
     * @param request HttpServletRequest 对象
     * @return Token 字符串，未找到返回 null
     */
    private String extractTokenFromRequest(HttpServletRequest request) {
        // 优先从 Authorization 头中获取[1,3](@ref)
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null) {
            if(authHeader.startsWith("Bearer ")){
                return authHeader.substring(7);
            }
            return authHeader;
        }

        // 其次从 token 参数中获取（兼容性考虑）
        String tokenParam = request.getParameter("token");
        if (tokenParam != null && !tokenParam.trim().isEmpty()) {
            return tokenParam.trim();
        }

        return null;
    }

    /**
     * 验证 Token 有效性
     *
     * @param token 待验证的 Token
     * @return true-有效，false-无效
     */
    private boolean validateToken(String token) {
        try {
            return token != null &&
                    !token.trim().isEmpty() &&
                    !"undefined".equals(token) &&
                    !"null".equals(token) &&
                    redisUtil.hasKey(RedisKey.LOGIN_USER + token);
        } catch (Exception e) {
            log.error("Token验证过程发生异常", e);
            return false;
        }
    }


    /**
     * 发送未授权响应
     *
     * @param response HttpServletResponse 对象
     * @return 总是返回 false，中断请求处理
     */
    private boolean sendUnauthorizedResponse(HttpServletResponse response, String msg) {
        try {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType("application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");

            response.getWriter().write(JSONUtil.toJsonStr(R.error(R.CODE_ERROR_AUTH,msg)));
            response.getWriter().flush();
        } catch (Exception e) {
            log.error("发送未授权响应失败", e);
        }

        return false;
    }
}

